package com.rags.chess.io;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collection;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.rags.chess.base.Color;
import com.rags.chess.base.Coordinate;
import com.rags.chess.base.Piece;
import com.rags.chess.base.PieceType;
import com.rags.chess.game.Game;
import com.rags.chess.game.Player;
import com.rags.chess.move.BasicMove;
import com.rags.chess.move.IllegalMoveException;
import com.rags.chess.move.KingsideCastling;
import com.rags.chess.move.Move;
import com.rags.chess.move.QueensideCastling;
import com.rags.chess.pieces.Rook;

public class PgnReader {
	private static Log log = LogFactory.getLog(PgnReader.class);

	private static String getHeaderValue(Collection<String> pgn, String headerName) {
		for (String line : pgn)
			if (line.toUpperCase().startsWith(String.format("[%s \"", headerName.toUpperCase())))
				return line.substring(line.indexOf('"')+1, line.lastIndexOf('"'));

		return null;
	}
	
	private static String removeComments(String pgn) {
		StringBuilder stringBuilder = new StringBuilder();
		int index = -1;
		while ((index = pgn.indexOf('{')) != -1) {
			stringBuilder.append(pgn.substring(0, index));
			pgn = pgn.substring(pgn.indexOf('}', index) + 1);
		}
		stringBuilder.append(pgn);
		return stringBuilder.toString();
	}
	
	private static String getGameText(Vector<String> pgn) {
		boolean headers = true;
		StringBuilder stringBuilder = new StringBuilder();
		for (int i=0; i<pgn.size(); i++) {
			if (pgn.elementAt(i) == null || pgn.elementAt(i).trim().length() == 0) continue;
			if (headers && pgn.elementAt(i).startsWith("[")) continue;
			if (headers) headers = false;
			
			stringBuilder.append(pgn.elementAt(i));
		}
		
		return removeComments(stringBuilder.toString());
	}
	
	private static Vector<String> getMoveList(String game) {
		Vector<String> movesList = new Vector<String>();
		String[] moves = game.split(" ");
		for (String move : moves) {
			int index = move.indexOf('.');
			if (index != -1)
				movesList.add(move.substring(index+1));
			else if (!move.matches(".*[a-z]+.*"))
				break;
			else
				movesList.add(move);
		}
		return movesList;
	}
	
	private static Player getWhitePlayerForGame(Vector<String> moveList) {
		final Vector<String> whiteMoves = new Vector<String>();
		for (int i=0; i<moveList.size(); i+=2)
			if (moveList.elementAt(i) != null)
				whiteMoves.add(moveList.elementAt(i));
		
		Player whitePlayer = new Player() {
			public Color getColor() {
				return Color.WHITE;
			}

			public String getName() {
				return null;
			}

			public Move makeMove(Game g) {
				String move = whiteMoves.elementAt(0);
				whiteMoves.remove(0);
				if (move.equals("O-O"))
					return new KingsideCastling(g, new Coordinate("e1"), new Coordinate("g1"));
				else if (move.equals("O-O-O"))
					return new QueensideCastling(g, new Coordinate("e1"), new Coordinate("c1"));
				else if (move.startsWith("R")) {
					Collection<Rook> rooks = new Vector<Rook>();
					Collection<Piece> pieces = g.getPieces(getColor());
					for (Piece p : pieces)
						if (p instanceof Rook)
							rooks.add((Rook)p);
					String destination = move.substring(move.length()-2);
					String source = move.substring(1, move.length()-2);
					for (Rook r : rooks) {
						if (source.length() > 0 && r.getNotation().contains(source)) {
							source = r.getNotation();
							break;
						} else if (source.length() == 0) {
							try {
								new BasicMove(g, new Coordinate(r.getNotation()), new Coordinate(destination));
								source = r.getNotation();
								break;
							} catch (IllegalMoveException e) {} 
						}
					}
					return new BasicMove(g, new Coordinate(source), new Coordinate(destination));
				}
				return null;
			}

			public PieceType returnPawnPromotionPiece() {
				return null;
			}
		};
		return whitePlayer;
	}
	
	private static Player getBlackPlayerForGame(Vector<String> moveList) {
		final Vector<String> blackMoves = new Vector<String>();
		for (int i=0; i<moveList.size(); i+=2)
			if (moveList.elementAt(i) != null)
				blackMoves.add(moveList.elementAt(i));
		
		Player blackPlayer = new Player() {
			public Color getColor() {
				return Color.BLACK;
			}

			public String getName() {
				return null;
			}

			public Move makeMove(Game g) {
				String move = blackMoves.elementAt(0);
				blackMoves.remove(0);
				if (move.equals("O-O"))
					return new KingsideCastling(g, new Coordinate("e8"), new Coordinate("g8"));
				return null;
			}

			public PieceType returnPawnPromotionPiece() {
				return null;
			}
		};
		return blackPlayer;
	}
	
	public static Game readGameFromFile(String filename) {
		Vector<String> fileText = new Vector<String>();
		try {
			BufferedReader bufferedReader = new BufferedReader(new FileReader(filename));
			String line = null;
			while ((line = bufferedReader.readLine()) != null)
				fileText.add(line);
		} catch (IOException e) {
			log.error(String.format("Unable to read from file: %s", filename));
			return null;
		}
		
		String whitePlayerName = getHeaderValue(fileText, Color.WHITE.toString());
		String blackPlayerName = getHeaderValue(fileText, Color.BLACK.toString());
		String result = getHeaderValue(fileText, "Result");
		String gameText = getGameText(fileText);
		log.debug(String.format("White: %s, Black: %s, Result: %s", whitePlayerName, blackPlayerName, result));
		log.debug(String.format("Game: %s", gameText));
		
		Vector<String> moveList = getMoveList(gameText);
		log.debug(String.format("Moves: %s", moveList));
		
		Game game = new Game(getWhitePlayerForGame(moveList), getBlackPlayerForGame(moveList));
		game.startGame();
		
		return game;
	}
}
